#include "common.h"

static int isExtensionCriticalInCrl(X509_CRL *crl, int extIndex);
static int isExtensionCritical(X509_EXTENSION *ext);

int main(int argc, char *argv[])
{

    int ret, len;
    unsigned char *buf, *p;
    unsigned long e = RSA_3;

    FILE *fp;
    time_t t;
    X509_NAME *issuer;
    ASN1_TIME *lastUpdate, *nextUpdate, *rvTime;
    X509_CRL *crl = NULL;
    X509_REVOKED *revoked;
    EVP_PKEY *pkey;
    ASN1_INTEGER *serial;
    RSA *r;
    BIGNUM *bne;
    BIO *bp;

    /* 生成密钥*/
    bne = BN_new();
    ret = BN_set_word(bne, e);
    r = RSA_new();

    ret = RSA_generate_key_ex(r, 1024, bne, NULL);
    if (ret != 1)
    {
        printf("RSA_generate_key_ex err!\n");
        return -1;
    }

    pkey = EVP_PKEY_new();

    EVP_PKEY_assign_RSA(pkey, r);

    crl = X509_CRL_new();

    /* 设置版本*/

    ret = X509_CRL_set_version(crl, 3);

    /* 设置颁发者*/

    issuer = X509_NAME_new();

    ret = X509_NAME_add_entry_by_NID(issuer, NID_commonName, V_ASN1_PRINTABLESTRING, "CRL issuer", 10, -1, 0);

    ret = X509_CRL_set_issuer_name(crl, issuer);

    /* 设置上次发布时间*/

    lastUpdate = ASN1_TIME_new();

    t = time(NULL);

    ASN1_TIME_set(lastUpdate, t);

    ret = X509_CRL_set_lastUpdate(crl, lastUpdate);

    /* 设置下次发布时间*/

    nextUpdate = ASN1_TIME_new();

    t = time(NULL);

    ASN1_TIME_set(nextUpdate, t + 1000);

    ret = X509_CRL_set_nextUpdate(crl, nextUpdate);

    // crl添加一个critical unsupport的扩展
    // 创建 Extension 对象
    uint8_t crlExtdata[] = "crlExt test";
    ASN1_BIT_STRING crlExtBitStr = {4, V_ASN1_BIT_STRING, crlExtdata, 0};
    X509_EXTENSION *NetscapeCommentCrlExt = X509V3_EXT_i2d(NID_netscape_comment, 1, (void *)(&crlExtBitStr));
    ret = X509_CRL_add_ext(crl, NetscapeCommentCrlExt, 0);

    isExtensionCritical(NetscapeCommentCrlExt);
    isExtensionCriticalInCrl(crl, 0);

    /* 添加被撤销证书序列号*/
    revoked = X509_REVOKED_new();
    serial = ASN1_INTEGER_new();
    ret = ASN1_INTEGER_set(serial, 43981);
    ret = X509_REVOKED_set_serialNumber(revoked, serial);
    rvTime = ASN1_TIME_new();
    t = time(NULL);

    ASN1_TIME_set(rvTime, t + 2000);
    ret = X509_CRL_set_nextUpdate(crl, rvTime);
    ret = X509_REVOKED_set_revocationDate(revoked, rvTime);
    ret = X509_CRL_add0_revoked(crl, revoked);

    // 创建 Extension 对象
    uint8_t data[] = "crlEntryExt test";
    ASN1_BIT_STRING bitStr = {4, V_ASN1_BIT_STRING, data, 0};
    X509_EXTENSION *NetscapeCommentExt = X509V3_EXT_i2d(NID_netscape_comment, 1, (void *)(&bitStr));
    int nRet = X509_REVOKED_add_ext(revoked, NetscapeCommentExt, 1);
    if (0 == nRet)
    {
        printf("X509_REVOKED_add_ext error, at line %d.\n", __LINE__);
        return CF_ERR_CRYPTO_OPERATION;
    }
    isExtensionCritical(NetscapeCommentExt);

    /* 排序*/

    ret = X509_CRL_sort(crl);

    /* 签名*/

    ret = X509_CRL_sign(crl, pkey, EVP_md5());

    /* 写入文件*/

    bp = BIO_new(BIO_s_file());

    BIO_set_fp(bp, stdout, BIO_NOCLOSE);

    X509_CRL_print(bp, crl);

    len = i2d_X509_CRL(crl, NULL);

    buf = malloc(len + 10);

    p = buf;

    len = i2d_X509_CRL(crl, &p);

    fp = fopen("crl.crl", "wb");

    fwrite(buf, 1, len, fp);

    fclose(fp);

    BIO_free(bp);

    // 重新生成CRL文件
    FILE *crlNewfile = fopen("newCrl20231114.pem", "w");
    if (!crlNewfile)
    {
        printf("can not create CRL new file\n");
        return 1;
    }

    PEM_write_X509_CRL(crlNewfile, crl);
    fclose(crlNewfile);

    X509_CRL_free(crl);

    free(buf);

    getchar();

    return 0;
}

static int isExtensionCriticalInCrl(X509_CRL *crl, int extIndex)
{
    // 获取扩展
    X509_EXTENSION *ext = X509_CRL_get_ext(crl, extIndex);

    int crit = X509_EXTENSION_get_critical(ext);
    if (crit)
    {
        printf("Extension with NID %d is critical in crl\n", extIndex);
        return 0;
    }
    else
    {
        printf("Extension with NID %d is not critical in crl,crit=%d\n", extIndex, crit);
    }

    return -1;
}

static int isExtensionCritical(X509_EXTENSION *ext)
{
    int crit = X509_EXTENSION_get_critical(ext);
    if (crit)
    {
        printf("Extension is critical\n");
        return 0;
    }
    else
    {
        printf("Extension is not critical,crit=%d\n", crit);
    }

    return -1;
}
